#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <iostream>
#include <cstring>
#include <memory>
#include <cstdio>

using std::cout;
using std::endl;
using std::strlen;
using std::unique_ptr;
using std::printf;

volatile bool pcf8591_dev_discon = true;     //pcf8591设备是否断开连接
volatile bool net_is_con = true;   //网络是否处于连接状态
volatile bool off_line_dev_con = false; //离线数据设备是否处于连接状态

static bool is_con_onenet = false;

/* 设置连接阿里云云平台按键的文本 */
void Widget::set_aliyun_con_btn_text(QString text)
{
    ui->aliyun_con_btn->setText(text);
}

dtu_4G *Widget::getDtu_4g_dev() const
{
    return dtu_4g_dev;
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    /* 使用系统指令比特率初始化CAN,默认为1000000bits/s */
    system("ifconfig can0 down");
    system("ip link set up can0 type can bitrate 1000000");
    this->pcf8591_mqtt_dev = new mqtt_dev(ONENET_ADDRESS,DATA_DEV_CLIENT_ID.constData(),MQTTCLIENT_PERSISTENCE_NONE,nullptr);
    this->conn_opts = MQTTClient_connectOptions_initializer;
    this->will_opts = MQTTClient_willOptions_initializer;
    /* 实例化4GDTU模块对象 */
    this->dtu_4g_dev = new dtu_4G(static_cast<QString>("ttyS2"),this);
    this->pub_thread = new pub_aliyun_thread(this->pcf8591_mqtt_dev->get_client(),this->dtu_4g_dev);
    this->manager = new QNetworkConfigurationManager(this);
    this->can_config = new dialog_can_config(this);
    this->aliyun_link_mode = new set_aliyun_link_dialog(this);
    this->off_line_config = new dialog_off_line_config(this);
    this->link_else_loongson = new link_else_loongson_dialog(this);
    /* 关联信号与槽 */
    connect(this->manager,&QNetworkConfigurationManager::onlineStateChanged,this,&Widget::net_dis_handler);
    connect(this->pub_thread,&pub_aliyun_thread::get_data_ok,this,&Widget::show_data);
    connect(this->dtu_4g_dev,&dtu_4G::aly_serianet_connect_ok,this,&Widget::dtu_4g_connect_aly_ok);
    connect(this->dtu_4g_dev,&dtu_4G::dev_reset_ok_signal,this,&Widget::dtu_4g_dev_reset_ok);
    connect(this->dtu_4g_dev,&dtu_4G::aly_disconnect,this,&Widget::dtu_4g_disconnect_aly);

    /* 启动采集与上传线程 */
    if(!this->pub_thread->isRunning())
    {
        this->pub_thread->set_get_data_state(true);
        this->pub_thread->start();
    }
}

Widget::~Widget()
{
    delete ui;
    this->pcf8591_mqtt_dev->disconnect();
    if(this->pub_thread->isRunning())
    {
        this->pub_thread->set_get_data_state(false);
        this->pub_thread->wait();
    }
    else if(!this->pub_thread->isFinished())
    {
        this->pub_thread->set_get_data_state(false);
    }
    delete this->pub_thread;
    if(this->off_line_dev)
        delete this->off_line_dev;
}

/**
  @description:接收消息回调函数
*/
static int pcf8591_msgarrvd(void *context, char *topicName, int topicLen,
            MQTTClient_message *message)
{
    (void)context;
    (void)topicLen;

    /* 释放占用的内存空间 */
    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);

    /* 退出 */
    return 1;
}

/**
  @description:断开连接回调函数
*/
static void pcf8591_connlost(void *context, char *cause)
{
    Widget *obj = (Widget*)context;
    obj->set_aliyun_con_btn_text("连接云平台");
    QString digTitle = "information消息框";
    QString strInfo = QString::asprintf("cause: %s",cause);
    QMessageBox::information(nullptr,digTitle,strInfo,QMessageBox::Ok,QMessageBox::NoButton);
    pcf8591_dev_discon = true;
}

/**
  @description:连接云平台
*/
void Widget::on_aliyun_con_btn_clicked()
{
    //是否已经连接云平台
    qint8 connect_mode = this->aliyun_link_mode->getAliyun_link_mode();
    if(connect_mode == set_aliyun_link_dialog::Ethernet_mode)
    {
        if(is_con_onenet == false)
        {
            //设置回调函数
            this->pcf8591_mqtt_dev->setCallback(this,pcf8591_connlost,pcf8591_msgarrvd,nullptr);
            //连接云平台
            this->will_opts.topicName = DATA_DEV_TOPIC;	//遗嘱主题
            this->will_opts.message = "Unexpected disconnection";//遗嘱消息
            this->will_opts.retained = 1;	//保留消息
            this->will_opts.qos = 0;		//QoS0

            conn_opts.will = &will_opts;
            this->conn_opts.keepAliveInterval = 30;	//心跳包间隔时间
            this->conn_opts.cleansession = 0;			//cleanSession标志
            this->conn_opts.username = DATA_DEV_USERNAME.constData();		//用户名
            this->conn_opts.password = DATA_DEV_PASSWORD.constData();		//密码
            if(this->pcf8591_mqtt_dev->connect(&this->conn_opts) == MQTTCLIENT_SUCCESS)
            {
                ui->aliyun_con_btn->setText("断开云平台");
                is_con_onenet = true;
                pcf8591_dev_discon = false;
                this->pub_thread->set_is_pub_date(true);
            }
        }
        else
        {
            this->disconnect();
            ui->aliyun_con_btn->setText("连接云平台");
            is_con_onenet = false;
            pcf8591_dev_discon = true;
        }
    }
    else if(connect_mode == set_aliyun_link_dialog::dtu_4G_mode)
    {
        if(is_con_onenet == false)
        {
            printf("4GDTU Connect\n");
            struct dtu_4G_aliyun_data t{};
            t.ProductKey = "a1fhO3qSWHg";
            t.DeviceName = "pcf8591";
            t.DeviceSecret = "ded479110c9d98348d7ba7025d024b87";
            t.Sub_Topic = "/a1fhO3qSWHg/pcf8591/user/get";
            t.Pub_Topic = "/sys/a1fhO3qSWHg/pcf8591/thing/event/property/post";
            t.territory = "cn-shanghai";
            t.CON = "\"0\",\"0\",\"0\",\"30\"";
            this->dtu_4g_dev->aliyun_serianet_connect(t);
        }
        else
        {
            this->dtu_4g_dev->aliyun_disconnect();
            ui->aliyun_con_btn->setText("连接云平台");
            is_con_onenet = false;
            pcf8591_dev_discon = true;
        }
    }
    else
    {

    }
}

/* 输出PWM */
void Widget::on_pwm_out_btn_clicked()
{
    if(this->pwm == nullptr)
    {
        this->pwm = new gpio_pwm(this);
        cout << "pwm out " << endl;
    }
    else
    {
        this->pwm->pwm_out_start();
        cout << "pwm out" << endl;
    }
    ui->pwm_out_btn->setDisabled(true);
    ui->pwm_stop_out_btn->setEnabled(true);
}

/* 调整pwm输出占空比函数 */
void Widget::on_pwm_duty_sli_valueChanged(int value)
{
    ui->pwm_duty_edit->setText(QString::asprintf("占空比: %d%%",value));
    qint64 period = this->pwm->get_period();
    qint64 new_duty = static_cast<qint64>(static_cast<float>(period) * (static_cast<float>(value) * 0.01));
    this->pwm->set_duty(new_duty);
}

/* 停止输出PWM */
void Widget::on_pwm_stop_out_btn_clicked()
{
    this->pwm->pwm_out_stop();
    ui->pwm_stop_out_btn->setDisabled(true);
    ui->pwm_out_btn->setEnabled(true);
    cout << "pwm out stop" << endl;
}

/* 离线功能设置 */
void Widget::on_set_off_line_btn_clicked()
{
    if(this->off_line_config->exec() == QDialog::Accepted)
    {
        qint8 tmp = this->off_line_config->get_duration();
        qint16 duration = 900 * (tmp+1);
        this->pub_thread->setCache_data_size_max(duration);
    }
}

/* 网络断开处理槽函数 */
void Widget::net_dis_handler(bool isOnline)
{
    cout << "Network is" << (isOnline ? "online" : "offline") << endl;
    net_is_con = isOnline;
    static quint8 cnt = 0;
    if(cnt == 0)
    {
        cnt++;
        if(isOnline == false)
        {
            this->timer = new QTimer(this);
            this->timer->stop();
            this->timer->setInterval(500);
            connect(this->timer,&QTimer::timeout,this,&Widget::off_line_data_pub);
            QMessageBox::warning(nullptr,"warning","网络连接已断开");
        }
    }
    else
    {
        if(isOnline == true)
        {
            this->timer->start();
        }
        else
        {
            this->timer->stop();
        }
    }
}

/**
  @description:断开连接回调函数
*/
static void off_line_connlost(void *context, char *cause)
{
    quint8 *cnt = (quint8*)context;
    (void)cause;

    *cnt = 0;
    off_line_dev_con = false;
}

/**
  @description:接收消息回调函数
*/
static int off_line_msgarrvd(void *context, char *topicName, int topicLen,
            MQTTClient_message *message)
{
    (void)context;
    (void)topicLen;

    /* 释放占用的内存空间 */
    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);

    /* 退出 */
    return 1;
}

/* 离线缓存数据上传 */
void Widget::off_line_data_pub()
{
    static quint8 cnt = 0;

    if(cnt == 0 && !off_line_dev_con)
    {
        cnt++;
        this->off_line_dev = new mqtt_dev(ONENET_ADDRESS,OFF_LINE_DATA_CLIENT_ID,MQTTCLIENT_PERSISTENCE_NONE,nullptr);
        //设置回调函数
        this->off_line_dev->setCallback(&cnt,off_line_connlost,off_line_msgarrvd,nullptr);
        //连接云平台
        this->will_opts.topicName = OFF_LINE_DATA_TOPIC;	//遗嘱主题
        this->will_opts.message = "Unexpected disconnection";//遗嘱消息
        this->will_opts.retained = 1;	//保留消息
        this->will_opts.qos = 0;		//QoS0

        conn_opts.will = &will_opts;
        this->conn_opts.keepAliveInterval = 30;	//心跳包间隔时间
        this->conn_opts.cleansession = 0;			//cleanSession标志
        this->conn_opts.username = OFF_LINE_DATA_USERNAME;		//用户名
        this->conn_opts.password = OFF_LINE_DATA_PASSWORD;		//密码
        if(this->off_line_dev->connect(&this->conn_opts) == MQTTCLIENT_SUCCESS)
        {
            off_line_dev_con = true;
            cout << "connect off-line_data dev" << endl;
        }
    }
    else if(cnt >= 1 && off_line_dev_con)
    {
        /* 上传离线数据 */
        QVector<struct gather_data> *data_vessel = this->pub_thread->get_cache_vessel();
        struct gather_data one_data = data_vessel->front();
        if(data_vessel->size() > 0)
        {
            MQTTClient_message pubmsg = MQTTClient_message_initializer;
            int rc = 0;
            char *ident = const_cast<char*>("voltage");
            char *cstr = OFF_LINE_DATA_PUB_DATA(ident,one_data.voltage);
            pubmsg.payload = cstr;	//消息的内容
            pubmsg.payloadlen = strlen(cstr);   //内容的长度
            pubmsg.qos = 0;				//QoS等级
            pubmsg.retained = 1;		//保留消息
            if((rc = MQTTClient_publishMessage(*this->off_line_dev->get_client(),OFF_LINE_DATA_PUB_TOPIC,&pubmsg,NULL)) != MQTTCLIENT_SUCCESS)
            {
                printf("off-line %s data pub failed\n",ident);
            }
            ident = const_cast<char*>("electricity");
            cstr = OFF_LINE_DATA_PUB_DATA(ident,one_data.electricity);
            pubmsg.payload = cstr;
            pubmsg.payloadlen = strlen(cstr);
            if((rc = MQTTClient_publishMessage(*this->off_line_dev->get_client(),OFF_LINE_DATA_PUB_TOPIC,&pubmsg,NULL)) != MQTTCLIENT_SUCCESS)
            {
                printf("off-line %s data pub failed\n",ident);
            }
            ident = const_cast<char*>("CurrentTemperature");
            cstr = OFF_LINE_DATA_PUB_DATA(ident,one_data.humiture[0]);
            pubmsg.payload = cstr;
            pubmsg.payloadlen = strlen(cstr);
            if((rc = MQTTClient_publishMessage(*this->off_line_dev->get_client(),OFF_LINE_DATA_PUB_TOPIC,&pubmsg,NULL)) != MQTTCLIENT_SUCCESS)
            {
                printf("off-line %s data pub failed\n",ident);
            }
            ident = const_cast<char*>("Humidity");
            cstr = OFF_LINE_DATA_PUB_DATA(ident,one_data.humiture[1]);
            pubmsg.payload = cstr;
            pubmsg.payloadlen = strlen(cstr);
            if((rc = MQTTClient_publishMessage(*this->off_line_dev->get_client(),OFF_LINE_DATA_PUB_TOPIC,&pubmsg,NULL)) != MQTTCLIENT_SUCCESS)
            {
                printf("off-line %s data pub failed\n",ident);
            }
            data_vessel->pop_front();
            cout << "pub off-line data" << endl;
        }
        else
        {
            this->timer->stop();
        }
    }
}

/* 显示数据 */
void Widget::show_data(struct gather_data val)
{
    ui->temp_edit->setText(QString::asprintf("%.1f",val.humiture[0]));
    ui->voltage_edit->setText(QString::asprintf("%.1f",val.voltage));
    ui->humi_edit->setText(QString::asprintf("%.1f",val.humiture[1]));
    ui->elec_edit->setText(QString::asprintf("%.1f",val.electricity));
}

/* 连接或断开CAN */
void Widget::on_can_con_btn_clicked()
{
    if(ui->can_con_btn->text() == "连接CAN")
    {
        /* Qt中的QCanBusDevice::BitRateKey不能设置比特率 */
        QString cmd1 = tr("ifconfig %1 down")
                .arg(this->can_config->get_port());
        QString cmd2 =
                tr("ip link set up %1 type can bitrate %2")
                .arg(this->can_config->get_port())
                .arg(this->can_config->get_baud());
        /* 使用系统指令以设置的比特率初始化CAN */
        system(cmd1.toStdString().c_str());
        system(cmd2.toStdString().c_str());

        QString errorString;
        /* 以设置的插件名与接口实例化candev */
        this->can_dev = QCanBus::instance()->createDevice(this->can_config->get_plugins_type(),this->can_config->get_port(),&errorString);
        if(!this->can_dev)
        {
            QMessageBox::information(nullptr,"CAN设备创建失败",errorString);
            this->can_config->set_state_lab(
                        tr("Error creating device '%1', reason: '%2'")
                        .arg(this->can_config->get_plugins_type())
                    .arg(errorString));
            return;
        }
        /* 连接CAN */
        if(!this->can_dev->connectDevice())
        {
            QMessageBox::information(nullptr,"CAN设备连接失败",this->can_dev->errorString());
            this->can_config->set_state_lab(tr("Connection error: %1")
                                            .arg(this->can_dev->errorString()));
            delete this->can_dev;
            this->can_dev = nullptr;

            return;
        }
        /* 关联信号与槽 */
        connect(this->can_dev,&QCanBusDevice::framesReceived,this,&Widget::receivedFrames);
        connect(this->can_dev,
                SIGNAL(errorOccurred(QCanBusDevice::CanBusError)),
                this,
                SLOT(canDeviceErrors(QCanBusDevice::CanBusError)));
        /* 更新CAN配置状态 */
        this->can_config->set_state_lab(
                    tr("插件类型为: %1, 已连接到 %2, 比特率为 %3 kBit/s")
                    .arg(this->can_config->get_plugins_type())
                .arg(this->can_config->get_port())
                .arg(this->can_config->get_baud().toInt() / 1000));
        ui->can_con_btn->setText("断开CAN");
        ui->set_can_config_btn->setDisabled(true);
    }
    //断开CAN
    else
    {
        this->can_dev->disconnectDevice();
        delete this->can_dev;
        this->can_dev = nullptr;
        ui->can_con_btn->setText("连接CAN");
        ui->set_can_config_btn->setEnabled(true);
        this->can_config->set_state_lab("未连接!");
    }
}

static QString frameFlags(const QCanBusFrame &frame)
{
    /* 格式化接收到的消息 */
    QString result = QLatin1String(" --- ");

    if (frame.hasBitrateSwitch())
        result[1] = QLatin1Char('B');
    if (frame.hasErrorStateIndicator())
        result[2] = QLatin1Char('E');
    if (frame.hasLocalEcho())
        result[3] = QLatin1Char('L');

    return result;
}

/* CAN接收消息 */
void Widget::receivedFrames()
{
    if (!can_dev)
        return;

    /* 读取帧 */
    while (can_dev->framesAvailable()) {
        const QCanBusFrame frame = can_dev->readFrame();
        QString view;
        if (frame.frameType() == QCanBusFrame::ErrorFrame)
            view = can_dev->interpretErrorFrame(frame);
        else
            view = frame.toString();

        const QString time = QString::fromLatin1("%1.%2  ")
                .arg(frame.timeStamp()
                     .seconds(), 10, 10, QLatin1Char(' '))
                .arg(frame.timeStamp()
                     .microSeconds() / 100, 4, 10, QLatin1Char('0'));

        const QString flags = frameFlags(frame);
        /* 接收消息框追加接收到的消息 */
        ui->can_data_recv_edit->insertPlainText(time + flags + view + "\n");
    }
}

/* 处理CAN错误 */
void Widget::canDeviceErrors(QCanBusDevice::CanBusError error) const
{
    /* 错误处理 */
    switch (error) {
    case QCanBusDevice::ReadError:
    case QCanBusDevice::WriteError:
    case QCanBusDevice::ConnectionError:
    case QCanBusDevice::ConfigurationError:
    case QCanBusDevice::UnknownError:{
        this->can_config->set_state_lab(this->can_dev->errorString());
        ui->can_recv_err_hint->setText(this->can_dev->errorString());
    };
        break;
    default:
        ui->can_recv_err_hint->setText(this->can_dev->errorString());
        break;
    }
}

/* CAN发送消息 */
void Widget::on_can_data_send_btn_clicked()
{
    if (!this->can_dev)
        return;
    /* 读取QLineEdit的文件 */
    QString str = ui->can_data_send_edit->toPlainText();
    QByteArray data = 0;
    QString strTemp = nullptr;
    /* 以空格分隔lineEdit的内容，并存储到字符串链表中 */
    QStringList strlist = str.split(' ');
    for (int i = 1; i < strlist.count(); i++) {
        strTemp = strTemp + strlist[i];
    }
    /* 将字符串的内容转为QByteArray类型 */
    data = QByteArray::fromHex(strTemp.toLatin1());

    bool ok;
    /* 以16进制读取要发送的帧内容里第一个数据，并作为帧ID */
    int framId = strlist[0].toInt(&ok, 16);
    QCanBusFrame frame = QCanBusFrame(framId, data);
    /* 写入帧 */
    this->can_dev->writeFrame(frame);
}

/* 配置CAN */
void Widget::on_set_can_config_btn_clicked()
{
    if(this->can_config->exec() == QDialog::Accepted)
    {

    }
}

/* 云平台连接设置 */
void Widget::on_aliyun_connect_config_btn_clicked()
{
    if(this->aliyun_link_mode->exec() == QDialog::Accepted)
    {

    }
}

/* 4GDTU模块成功连接阿里云 */
void Widget::dtu_4g_connect_aly_ok()
{
    ui->aliyun_con_btn->setText("断开云平台");
    is_con_onenet = true;
    pcf8591_dev_discon = false;
    this->pub_thread->set_is_pub_date(true);
}

/* 4GDTU模块断开与阿里云的连接 */
void Widget::dtu_4g_disconnect_aly()
{
    ui->aliyun_con_btn->setText("连接云平台");
    pcf8591_dev_discon = true;
}

/* 重置并重启4GDTU设备 */
void Widget::on_dtu_4g_reset_btn_clicked()
{
    ui->dtu_4g_reset_btn->setDisabled(true);
    this->dtu_4g_dev->dtu_4g_reset();
}

void Widget::dtu_4g_dev_reset_ok()
{
    ui->dtu_4g_reset_btn->setEnabled(true);
}

/* 连接其他龙芯派 */
void Widget::on_link_else_loongson_clicked()
{
//    this->link_else_loongson->exec();
}
